﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Web.Mvc;
using System.Web.Security;
using CodeStash.Common.DataAccess.EntityFramework;
using CodeStash.Common.DataAccess.Repository;
using CodeStash.Common.DataAccess.UnitOfWork;
using CodeStash.Common.Enums;
using CodeStash.Filters;
using CodeStash.Models.Snippet;
using CodeStash.Services;
using CodeStash.Utils;
using System.Text;
using System.Web;

namespace CodeStash.Controllers
{
    public class CodeSnippetController : BaseTagCloudEnabledController
    {
        private readonly ILoggerService loggerService;
        private readonly IMembershipService membershipService;
        private readonly IRepository<Language> languageRepository;
        private readonly IRepository<Visibility> visibilityRepository;
        private readonly IRepository<CodeCategory> categoryRepository;
        private readonly IRepository<Grouping> groupingRepository;
        private readonly IRepository<CodeSnippet> codeSnippetRepository;
        private readonly IRepository<CodeTag> codeTagRepository;
        private readonly IRepository<CreatedTeam> createdTeamRepository;
        private readonly IRepository<CodeSnippetForumMessage> codeSnippetForumMessageRepository;
        private readonly IUnitOfWork unitOfWork;



        public CodeSnippetController(
            ILoggerService loggerService,
            IMembershipService membershipService,
            ITagCloudService tagCloudService,
            IRepository<Language> languageRepository,
            IRepository<Visibility> visibilityRepository,
            IRepository<CodeCategory> categoryRepository,
            IRepository<Grouping> groupingRepository,
            IRepository<CodeSnippet> codeSnippetRepository,
            IRepository<CodeTag> codeTagRepository,
            IRepository<CodeSnippetForumMessage> codeSnippetForumMessageRepository,
            IUnitOfWork unitOfWork)
            : base(tagCloudService)
        {
            this.loggerService = loggerService;
            this.membershipService = membershipService;
            this.languageRepository = languageRepository;
            this.visibilityRepository = visibilityRepository;
            this.categoryRepository = categoryRepository;
            this.groupingRepository = groupingRepository;
            this.codeSnippetRepository = codeSnippetRepository;
            this.codeTagRepository = codeTagRepository;
            this.codeSnippetForumMessageRepository = codeSnippetForumMessageRepository;
            this.unitOfWork = unitOfWork;
        }


        [Authorize]
        [RenderTagCloud]
        [HttpGet]
        public ActionResult OpenFromWeb()
        {
            loggerService.Info("Codesnippet : OpenFromWeb GET");
            OpenFromWebViewModel vm = new OpenFromWebViewModel();
            vm.HighlightingCSS = CodeSnippetUtils.GetUsersSavedHighlightingCSS(User.Identity);
            AddLanguagesToOpenFromWebVm(vm);
            return View(vm);
        }


        [Authorize]
        [RenderTagCloud]
        [HttpPost]
        [AjaxOnly]
        [ValidateAntiForgeryToken(Salt = "OpenFromWeb")]
        public ActionResult OpenFromWeb(OpenFromWebViewModel vm)
        {
            try
            {
                loggerService.Info(string.Format("Codesnippet : OpenFromWeb POST called with FileName {0}, Language {1} ",
                    vm.FileName, vm.LanguageId));

                //example url "https://raw.github.com/codai/Sharp-Lite/master/SharpLiteSrc/app/SharpLite.EntityFrameworkProvider/DbContext.cs";
                OpenFromWebViewModel newVm = new OpenFromWebViewModel();
                newVm.HighlightingCSS = CodeSnippetUtils.GetUsersSavedHighlightingCSS(User.Identity);
                newVm.ActualCode = CodeSnippetUtils.ReadContentFromWebUrl(vm.FileName);
                AddLanguagesToOpenFromWebVm(newVm);
                newVm.LanguageId = vm.LanguageId;
                newVm.CodeHasBeenParsed = ModelState.IsValid && !string.IsNullOrWhiteSpace(newVm.ActualCode);
                return PartialView("OpenFromWebPartial", newVm);
            }
            catch (Exception ex)
            {
                loggerService.Error(string.Format("Codesnippet : OpenFromWeb POST Exception has occurred\r\n\r\n{0}\r\n\r\n{1}",
                    ex.Message, ex.StackTrace));
                OpenFromWebViewModel newVm = new OpenFromWebViewModel();
                newVm.HighlightingCSS = CodeSnippetUtils.GetUsersSavedHighlightingCSS(User.Identity);
                AddLanguagesToOpenFromWebVm(newVm);
                newVm.CodeHasBeenParsed = false;
                return PartialView("OpenFromWebPartial", newVm);
            }
        }



        //[Authorize]
        [RenderTagCloud]
        [HttpGet]
        public ActionResult Index()
        {
            loggerService.Info("Codesnippet : AddSnippet GET");
            AddSnippetViewModel vm = new AddSnippetViewModel();
            RefreshAddModelStaticData(vm);
            vm.Visibility = vm.VisibilityList.Where(x => x.VisibilityDescription == CodeSnippetVisibility.JustMe.ToString()).SingleOrDefault().Id;
            return View(vm);
        }


        [Authorize]
        [HttpPost]
        [ValidateInput(false)] // allow through code type text for this Action
        [AjaxOnly]
        [ValidateAntiForgeryToken(Salt = "Add")]
        public ActionResult Add(AddSnippetViewModel vm)
        {

            StringBuilder sbModel = new StringBuilder();
            sbModel.Append(string.Format("Title :{0}, ", vm.Title));
            sbModel.Append(string.Format("Description :{0}, ", vm.Description));
            sbModel.Append(string.Format("NewCodeCategoryName :{0}, ", vm.NewCodeCategoryName));
            sbModel.Append(string.Format("CodeCategoryId :{0}, ", vm.CodeCategoryId));
            sbModel.Append(string.Format("NewGroupingName :{0}, ", vm.NewGroupingName));
            sbModel.Append(string.Format("GroupId :{0}, ", vm.GroupId));
            sbModel.Append(string.Format("LanguageId :{0}, ", vm.LanguageId));
            sbModel.Append(string.Format("Tags :{0}, ", vm.Tags));
            sbModel.Append(string.Format("Visibility :{0}, ", vm.Visibility));
            sbModel.Append(string.Format("ActualCode :{0}, ", vm.ActualCode != null ? vm.ActualCode.Take(50) : ""));

            try
            {
                if (ModelState.IsValid)
                {
                    loggerService.Info(string.Format("Codesnippet : Attempting to add snippet, where AddSnippetViewModel was constructed as follows:\r\n{0}", sbModel.ToString()));
                    CodeSnippet addedSnippet = AddOrUpdateSnippet(vm, false);
                    ViewData["successfulAdd"] = true;
                    ViewData["addedSnippetId"] = addedSnippet.CodeSnippetId;
                    RefreshAddModelStaticData(vm);
                    return PartialView("AddSnippetPartial", vm);
                }
                else
                {
                    string errors = ModelState.Keys.Aggregate((x, y) => string.Format("{0}\r\n{1}", x, y));
                    loggerService.Error(string.Format("Codesnippet : The AddSnippetViewModel model was invalid, where AddSnippetViewModel was constructed as follows:\r\n{0}\r\nError keys were\r\n{1}", sbModel.ToString(), errors));
                    ViewData["successfulAdd"] = false;
                    ViewData["addedSnippetId"] = 0;
                    RefreshAddModelStaticData(vm);
                    return PartialView("AddSnippetPartial", vm);
                }

            }
            catch (Exception ex)
            {
                loggerService.Error(string.Format("Codesnippet : Add POST, An error occurred. Message :{0}, StackTrace: {1}", ex.Message, ex.StackTrace));
                RefreshAddModelStaticData(vm);
                ViewData["successfulAdd"] = false;
                return PartialView("AddSnippetPartial", vm);
            }
        }

        [Authorize]
        [RenderTagCloud]
        [HttpGet]
        public ActionResult Edit(int codeSnippetId)
        {
            loggerService.Info(string.Format("Codesnippet : Edit called for Snippet Id : {0}", codeSnippetId));

            EditSnippetViewModel vm;
            using (unitOfWork)
            {
                codeSnippetRepository.EnrolInUnitOfWork(unitOfWork);
                codeTagRepository.EnrolInUnitOfWork(unitOfWork);
                languageRepository.EnrolInUnitOfWork(unitOfWork);
                groupingRepository.EnrolInUnitOfWork(unitOfWork);
                categoryRepository.EnrolInUnitOfWork(unitOfWork);

                CodeSnippet snippet = codeSnippetRepository.FindBy(x => x.CodeSnippetId == codeSnippetId).SingleOrDefault();
                vm = new EditSnippetViewModel();
                RefreshAddModelStaticData(vm);
                vm.Visibility = vm.VisibilityList.Where(x => x.VisibilityDescription == CodeSnippetVisibility.JustMe.ToString()).SingleOrDefault().Id;
                if (snippet != null)
                {
                    loggerService.Info("Codesnippet : Located snippet for editing");

                    List<CodeSnippet> snippets = new List<CodeSnippet>();
                    snippets.Add(snippet);
                    snippets = AddLanguageCategoryAndGroupingLookups(snippets, languageRepository,
                                        groupingRepository, categoryRepository);

                    List<CodeTag> existingTags = codeTagRepository.FindBy(x => x.MatchingSnippetId == snippet.CodeSnippetId).ToList();
                    vm.PopulateFromExistingSnippet(snippets.First(), existingTags);
                }
                else
                {
                    loggerService.Error(string.Format("Codesnippet : Could not locate Snippet Id : {0}", codeSnippetId));
                }

            }
            return View("EditSnippet", vm);
        }


        [Authorize]
        [HttpPost]
        [ValidateInput(false)] // allow through code type text for this Action
        [AjaxOnly]
        [ValidateAntiForgeryToken(Salt = "Edit")]
        public ActionResult Edit(EditSnippetViewModel vm)
        {
            StringBuilder sbModel = new StringBuilder();
            sbModel.Append(string.Format("Title :{0}, ", vm.Title));
            sbModel.Append(string.Format("Description :{0}, ", vm.Description));
            sbModel.Append(string.Format("NewCodeCategoryName :{0}, ", vm.NewCodeCategoryName));
            sbModel.Append(string.Format("CodeCategoryId :{0}, ", vm.CodeCategoryId));
            sbModel.Append(string.Format("NewGroupingName :{0}, ", vm.NewGroupingName));
            sbModel.Append(string.Format("GroupId :{0}, ", vm.GroupId));
            sbModel.Append(string.Format("LanguageId :{0}, ", vm.LanguageId));
            sbModel.Append(string.Format("Tags :{0}, ", vm.Tags));
            sbModel.Append(string.Format("Visibility :{0}, ", vm.Visibility));
            sbModel.Append(string.Format("ActualCode :{0}, ", vm.ActualCode != null ? vm.ActualCode.Take(50) : ""));

            try
            {
                if (ModelState.IsValid)
                {
                    loggerService.Info(string.Format("Codesnippet : Attempting to Edit snippet, where EditSnippetViewModel was constructed as follows:\r\n{0}", sbModel.ToString()));
                    CodeSnippet addedSnippet = AddOrUpdateSnippet(vm, true);
                    ViewData["successfulEdit"] = true;
                    ViewData["editedSnippetId"] = addedSnippet.CodeSnippetId;
                    RefreshAddModelStaticData(vm);
                    return PartialView("EditSnippetPartial", vm);
                }
                else
                {
                    string errors = ModelState.Keys.Aggregate((x, y) => string.Format("{0}\r\n{1}", x, y));
                    loggerService.Error(string.Format("Codesnippet : The EditSnippetViewModel model was invalid, where EditSnippetViewModel was constructed as follows:\r\n{0}\r\nError keys were\r\n{1}", sbModel.ToString(), errors));
                    ViewData["successfulEdit"] = false;
                    ViewData["editedSnippetId"] = 0;
                    RefreshAddModelStaticData(vm);
                    return PartialView("EditSnippetPartial", vm);
                }

            }
            catch (Exception ex)
            {
                loggerService.Error(string.Format("Codesnippet : Edit POST, An error occurred. Message :{0}, StackTrace: {1}", ex.Message, ex.StackTrace));
                RefreshAddModelStaticData(vm);
                ViewData["successfulEdit"] = false;
                return PartialView("EditSnippetPartial", vm);
            }
        }





        //[Authorize]
        [RenderTagCloud]
        [HttpGet]
        public ActionResult DisplaySnippetsForAddAndEdit(int codeSnippetId, bool wasAddOrEdit = true)
        {

            loggerService.Info(string.Format("Codesnippet : DisplaySnippetsForAddAndEdit called with CodeSnippetId = {0}, wasAddOrEdit={1}",
                codeSnippetId, wasAddOrEdit));

            List<CodeSnippet> allSnippetsInGroup = new List<CodeSnippet>();
            Dictionary<int, bool> snippetPublicOrMineLookup = new Dictionary<int, bool>();
            bool truncated = false;

            using (unitOfWork)
            {
                codeSnippetRepository.EnrolInUnitOfWork(unitOfWork);
                languageRepository.EnrolInUnitOfWork(unitOfWork);
                groupingRepository.EnrolInUnitOfWork(unitOfWork);
                categoryRepository.EnrolInUnitOfWork(unitOfWork);

                CodeSnippet requestedSnippet = codeSnippetRepository.FindBy(x => x.CodeSnippetId == codeSnippetId).SingleOrDefault();
                if (requestedSnippet != null)
                {
                    allSnippetsInGroup.Add(requestedSnippet);
                    if (requestedSnippet.GroupId.HasValue && requestedSnippet.GroupId > 0)
                    {

                        Grouping grouping = groupingRepository.FindBy(x => x.GroupId == requestedSnippet.GroupId).FirstOrDefault();
                        loggerService.Info(string.Format("Codesnippet : Snippet '{0}' is in group '{1}', so obtaining grouped snippets", codeSnippetId, grouping.Description));

                        int countOfsnippetsSameAsRequestedGroupSnippets = codeSnippetRepository.FindByExp(x => x.GroupId.HasValue &&
                            requestedSnippet.GroupId.Value == requestedSnippet.GroupId && x.CodeSnippetId != requestedSnippet.CodeSnippetId).ToList().Count();
                        Func<CodeSnippet, bool> snippetFunc = x => x.GroupId.HasValue &&
                            x.GroupId.Value == requestedSnippet.GroupId &&
                            x.CodeSnippetId != requestedSnippet.CodeSnippetId;
                        List<CodeSnippet> sameAsRequestedGroupSnippets =
                                CodeSnippetUtils.TakeAsManySnippetsAsWeCan(ref truncated, codeSnippetRepository,
                                    snippetFunc, countOfsnippetsSameAsRequestedGroupSnippets);

                        allSnippetsInGroup.AddRange(sameAsRequestedGroupSnippets);
                        foreach (CodeSnippet snippet in allSnippetsInGroup)
                        {
                            snippet.Grouping = grouping;
                        }
                    }

                    allSnippetsInGroup = CodeSnippetUtils.AddLanguageCategoryLookups(allSnippetsInGroup, languageRepository, categoryRepository);
                }
                else
                {
                    loggerService.Error(string.Format("Codesnippet : Could not obtain snippet for snippetId : {0}", codeSnippetId));
                }
                snippetPublicOrMineLookup = ObtainAccessRightsForSnippets(allSnippetsInGroup, membershipService, User);
            }

            string highlightingCSSName = CodeSnippetUtils.GetUsersSavedHighlightingCSS(User.Identity);

            DisplaySnippetsViewModel vm = CreateWrappers(allSnippetsInGroup, truncated, snippetPublicOrMineLookup, highlightingCSSName);
            ViewData["WasAddOrEdit"] = wasAddOrEdit;
            return View("DisplaySnippets", vm);
        }



        //[Authorize]
        [RenderTagCloud]
        [HttpGet]
        public ActionResult DisplaySnippetsForCategory(string category)
        {
            List<CodeSnippet> allSnippetsInCategory = new List<CodeSnippet>();
            bool truncated = false;



            using (unitOfWork)
            {
                codeSnippetRepository.EnrolInUnitOfWork(unitOfWork);
                languageRepository.EnrolInUnitOfWork(unitOfWork);
                groupingRepository.EnrolInUnitOfWork(unitOfWork);
                categoryRepository.EnrolInUnitOfWork(unitOfWork);
                codeSnippetForumMessageRepository.EnrolInUnitOfWork(unitOfWork);

                CodeCategory requestedCategory = categoryRepository.FindBy(x => x.CodeCategoryName.Contains(category)).FirstOrDefault();


                if (requestedCategory != null)
                {
                    loggerService.Info(string.Format("Codesnippet : Attempting to display snippet for category '{0}'", requestedCategory.CodeCategoryName));
                    int countOfSnippetsInCategory = codeSnippetRepository.FindByExp(x => x.CodeCategoryId == requestedCategory.CodeCategoryId).Count();
                    Func<CodeSnippet, bool> snippetFunc = x => x.CodeCategoryId == requestedCategory.CodeCategoryId;
                    allSnippetsInCategory = CodeSnippetUtils.TakeAsManySnippetsAsWeCan(ref truncated, codeSnippetRepository,
                                                snippetFunc, countOfSnippetsInCategory).ToList();

                    allSnippetsInCategory = AddLanguageCategoryAndGroupingLookups(allSnippetsInCategory, languageRepository, groupingRepository, categoryRepository);

                    foreach (CodeSnippet snippet in allSnippetsInCategory)
                    {
                        snippet.CodeSnippetForumMessages.AddRange(codeSnippetForumMessageRepository.FindBy(x => x.CodeSnippetId == snippet.CodeSnippetId));
                    }
                }
                else
                {
                    loggerService.Error(string.Format("Codesnippet : Could not obtain category for category '{0}'", category));
                }
            }

            Dictionary<int, bool> snippetPublicOrMineLookup = ObtainAccessRightsForSnippets(allSnippetsInCategory, membershipService, User);

            string highlightingCSSName = CodeSnippetUtils.GetUsersSavedHighlightingCSS(User.Identity);

            DisplaySnippetsViewModel vm = CreateWrappers(allSnippetsInCategory, truncated, snippetPublicOrMineLookup, highlightingCSSName);
            ViewData["WasAddOrEdit"] = false;
            return View("DisplaySnippets", vm);
        }





        [Authorize]
        [HttpPost]
        [AjaxOnly]
        public JsonResult DeleteSnippetAndShowRemaining(DisplayMode displayMode,
            int snippetId, int groupId, bool deleteAllInGroup)
        {

            loggerService.Info(string.Format(
                "Codesnippet : DeleteSnippetAndShowRemaining was called with displayMode={0},snippetId={1},groupId={2},deleteAllInGroup={3}",
                displayMode, snippetId, groupId, deleteAllInGroup));


            using (unitOfWork)
            {
                codeTagRepository.EnrolInUnitOfWork(unitOfWork);
                codeSnippetRepository.EnrolInUnitOfWork(unitOfWork);
                groupingRepository.EnrolInUnitOfWork(unitOfWork);
                languageRepository.EnrolInUnitOfWork(unitOfWork);
                categoryRepository.EnrolInUnitOfWork(unitOfWork);

                MembershipUser user = membershipService.GetUserByUserName(User.Identity.Name);
                Guid userId = Guid.Parse(user.ProviderUserKey.ToString());


                if (deleteAllInGroup && groupId > 0)
                {
                    //only delete the snippets in the group, if the user asked us to delete all snippets
                    //in group and we own them all. Can't delete other peoples snippets
                    List<CodeSnippet> allSnippetsInGroup =
                        codeSnippetRepository.FindBy(x => x.GroupId == groupId && x.AspNetMembershipUserId == userId).ToList();
                    foreach (CodeSnippet snippet in allSnippetsInGroup)
                    {
                        DeleteSingleSnippetAndAllTags(snippet.CodeSnippetId, codeTagRepository, codeSnippetRepository);
                    }

                    groupingRepository.Remove(groupingRepository.FindBy(x => x.GroupId == groupId).First());
                    loggerService.Info(string.Format("Codesnippet : Deleting all snippets in groupId: {0}, that belong to current user", groupId));

                }
                else
                {
                    //only delete the single snippet if its ours to delete.
                    //Can't delete other peoples snippets
                    CodeSnippet snippetRequestedToDelete =
                        codeSnippetRepository.FindBy(x => x.CodeSnippetId == snippetId && x.AspNetMembershipUserId == userId).FirstOrDefault();
                    if (snippetRequestedToDelete != null)
                    {
                        DeleteSingleSnippetAndAllTags(snippetId, codeTagRepository, codeSnippetRepository);
                    }

                    int count = codeSnippetRepository.FindBy(x => x.GroupId == groupId).Count();
                    // We can only delete the repository if this is the last record.
                    if (count == 0)
                    {
                        // Only attempt to remove groupings if there are any.
                        Grouping grouping = groupingRepository.FindBy(x => x.GroupId == groupId).FirstOrDefault();
                        if (grouping != null)
                        {
                            groupingRepository.Remove(grouping);
                        }
                    }
                    loggerService.Info(string.Format("Codesnippet : Deleting snippetId :", snippetId));
                }

                unitOfWork.Commit();
            }
            return Json(true);
        }



        //[Authorize]
        [HttpPost]
        [AjaxOnly]
        public ActionResult TryFindCategory(string categoryName)
        {
            using (unitOfWork)
            {
                categoryRepository.EnrolInUnitOfWork(unitOfWork);
                List<CodeCategory> matchedCategories =
                    categoryRepository.FindBy(x => x.CodeCategoryName.ToLower().Contains(categoryName.ToLower())).ToList();

                if (matchedCategories.Any())
                {
                    loggerService.Info(string.Format("Codesnippet : TryFindCategory found match for category '{0}'", categoryName));
                    return Json(new { Message = matchedCategories, Success = true });
                }
                else
                {
                    loggerService.Error(string.Format("Codesnippet : TryFindCategory found no matches for category '{0}'", categoryName));
                    return Json(new { Message = "Couldn't find any categories matching your entered category, please retry or type a new one", Success = false });
                }
            }
        }


        //[Authorize]
        [HttpPost]
        [AjaxOnly]
        public ActionResult TryFindGrouping(string groupName)
        {
            using (unitOfWork)
            {
                groupingRepository.EnrolInUnitOfWork(unitOfWork);
                List<Grouping> matchedGrouping =
                    groupingRepository.FindBy(x => x.Description.ToLower().Contains(groupName.ToLower())).ToList();

                if (matchedGrouping.Count > 0)
                {
                    loggerService.Info(string.Format("Codesnippet : TryFindGrouping found match for group '{0}'", groupName));
                    return Json(new { Message = matchedGrouping, Success = true });
                }
                else
                {
                    loggerService.Error(string.Format("Codesnippet : TryFindGrouping found no matches for group '{0}'", groupName));
                    return Json(new { Message = "Couldn't find any groupings matching your entered group, please retry or type a new one", Success = false });
                }
            }
        }

        [AcceptVerbs("post")]
        public JsonResult SetRating(int codeSnippetId, int rating)
        {
            try
            {
                if (CanUserVote(codeSnippetId, rating) == false)
                {
                    return Json(new JsonRatingResponse
                    {
                        Success = false,
                        Message = "Sorry, you already voted for this snippet"
                    });
                }

                SnippetRatingModel snippetRatingModel = IncrementSnippetRating(codeSnippetId, rating);
                if (snippetRatingModel != null)
                {
                    return Json(new JsonRatingResponse
                    {
                        Success = true,
                        Message = "Your Vote was cast successfully",
                        Result = snippetRatingModel
                    });
                }
                else
                {
                    return Json(new JsonRatingResponse
                    {
                        Success = false,
                        Message = "There was a problem adding your rating"
                    });
                }
            }
            catch (Exception ex)
            {
                return Json(new JsonRatingResponse
                {
                    Success = false,
                    Message = ex.Message
                });
            }
        }

        #region Private Methods

        private Boolean CanUserVote(int codeSnippetId, double rating)
        {
            HttpCookie voteCookie = Request.Cookies["Votes"];

            if (voteCookie != null)
            {
                if (voteCookie[codeSnippetId.ToString()] != null)
                {
                    return false;
                }
            }

            //create the cookie and set the value
            voteCookie = new HttpCookie("Votes");
            voteCookie[codeSnippetId.ToString()] = rating.ToString();
            Response.Cookies.Add(voteCookie);
            return true;
        }

        private SnippetRatingModel IncrementSnippetRating(int codeSnippetId, int rating)
        {
            SnippetRatingModel snippetRatingModel = null;
            using (unitOfWork)
            {
                codeSnippetRepository.EnrolInUnitOfWork(unitOfWork);

                CodeSnippet codeSnippet = codeSnippetRepository.FindBy(x => x.CodeSnippetId == codeSnippetId).First();
                codeSnippet.Rating += rating;
                codeSnippet.TotalRatings += 1;
                unitOfWork.Commit();
                snippetRatingModel = new SnippetRatingModel(

                    codeSnippet.CodeSnippetId,
                    codeSnippet.Rating,
                    codeSnippet.TotalRatings,
                    Convert.ToDouble(codeSnippet.Rating) / Convert.ToDouble(codeSnippet.TotalRatings)
                );
            }

            return snippetRatingModel;

        }


        private static DisplaySnippetsViewModel CreateWrappers(List<CodeSnippet> allSnippetsInCategory, bool truncated, Dictionary<int, bool> snippetPublicOrMineLookup, String highlightingCSSName)
        {
            List<CodeSnippetWrapperViewModel> snippetWrappers = new List<CodeSnippetWrapperViewModel>();
            foreach (CodeSnippet snippet in allSnippetsInCategory)
            {
                snippetWrappers.Add(new CodeSnippetWrapperViewModel(snippetPublicOrMineLookup[snippet.CodeSnippetId], snippet));
            }

            DisplayMode mode = snippetWrappers.Count() > 0 ? DisplayMode.ByCategory : DisplayMode.SingleSnippet;
            DisplaySnippetsViewModel vm = new DisplaySnippetsViewModel(snippetWrappers, mode, truncated, highlightingCSSName);
            return vm;
        }

        private static List<CodeSnippet> AddLanguageCategoryAndGroupingLookups(
            List<CodeSnippet> codeSnippets,
            IRepository<Language> languageRepository,
            IRepository<Grouping> groupingRepository,
            IRepository<CodeCategory> categoryRepository)
        {
            foreach (CodeSnippet snippet in codeSnippets)
            {
                snippet.Language = languageRepository.FindBy(x => x.LanguageId == snippet.LanguageId).SingleOrDefault();
                snippet.CodeCategory = categoryRepository.FindBy(x => x.CodeCategoryId == snippet.CodeCategoryId).SingleOrDefault();
                if (snippet.GroupId.HasValue && snippet.GroupId > 0)
                {
                    snippet.Grouping = groupingRepository.FindBy(x => x.GroupId == snippet.GroupId).SingleOrDefault();
                }
            }
            return codeSnippets;
        }


        private void DeleteSingleSnippetAndAllTags(int codeSnippetId, IRepository<CodeTag> codeTagRepository, IRepository<CodeSnippet> codeSnippetRepository)
        {
            List<CodeTag> allTagsLinksToSnippet = codeTagRepository.FindBy(x => x.MatchingSnippetId == codeSnippetId).ToList();
            foreach (CodeTag tag in allTagsLinksToSnippet)
            {
                codeTagRepository.Remove(tag);
            }
            codeSnippetRepository.Remove(codeSnippetRepository.FindBy(x => x.CodeSnippetId == codeSnippetId).First());
        }




        private static List<CodeSnippet> GetAllSnippetsInGroup(IRepository<Grouping> groupingRepository,
            IRepository<CodeSnippet> codeSnippetRepository, int groupId)
        {

            Grouping grouping = groupingRepository.FindBy(x => x.GroupId == groupId).FirstOrDefault();

            List<CodeSnippet> remainingSnippetsInGroup = codeSnippetRepository.FindBy(x => x.GroupId.HasValue &&
                x.GroupId == groupId).ToList();

            foreach (CodeSnippet snippet in remainingSnippetsInGroup)
            {
                snippet.Grouping = grouping;
            }
            return remainingSnippetsInGroup;
        }


        private static Dictionary<int, bool> ObtainAccessRightsForSnippets(List<CodeSnippet> snippets,
            IMembershipService membershipService, IPrincipal userPrincipal)
        {
            Dictionary<int, bool> snippetPublicOrMineLookup = new Dictionary<int, bool>();
            foreach (CodeSnippet snippet in snippets)
            {
                MembershipUser user = membershipService.GetUserByUserName(userPrincipal.Identity.Name);
                Guid userId = Guid.Empty;
                if (user != null) userId = Guid.Parse(user.ProviderUserKey.ToString());
                bool isMySnippet = snippet.AspNetMembershipUserId == userId;

                snippetPublicOrMineLookup.Add(snippet.CodeSnippetId, isMySnippet);
            }
            return snippetPublicOrMineLookup;
        }


        private CodeSnippet AddOrUpdateSnippet(ISnippetViewModel vm, bool isEdit)
        {
            categoryRepository.EnrolInUnitOfWork(unitOfWork);
            groupingRepository.EnrolInUnitOfWork(unitOfWork);
            languageRepository.EnrolInUnitOfWork(unitOfWork);
            visibilityRepository.EnrolInUnitOfWork(unitOfWork);

            #region Look up or insert category
            CodeCategory category = null;

            IEnumerable<CodeCategory> categories;

            if (string.IsNullOrEmpty(vm.NewCodeCategoryName))
            {
                categories = new List<CodeCategory>();
            }
            else
            {
                categories = categoryRepository.FindBy(x => x.CodeCategoryName.Trim() == vm.NewCodeCategoryName.Trim());
            }

            if (categories.Any())
            {
                category = categories.First();
            }
            else
            {
                category = new CodeCategory(vm.NewCodeCategoryName.Trim());
                categoryRepository.Add(category);
            }
            #endregion

            #region Look up or insert group
            //NOTE : grouping are not mandaatory, so it may be possible to get to end of this
            //       section and still not create a Grouping, that is total valid
            Grouping grouping = null;

            IEnumerable<Grouping> groupings;

            if (string.IsNullOrEmpty(vm.NewGroupingName))
            {
                groupings = new List<Grouping>();
            }
            else
            {
                groupings = groupingRepository.FindBy(x => x.Description.Trim() == vm.NewGroupingName.Trim());
            }

            if (groupings.Any())
            {
                grouping = groupings.First();
            }
            else
            {
                if (!String.IsNullOrEmpty(vm.NewGroupingName))
                {
                    grouping = new Grouping(vm.NewGroupingName.Trim());
                    groupingRepository.Add(grouping);
                }
            }
            #endregion

            //Look up language
            Language language = languageRepository.FindBy(x => x.LanguageId == vm.LanguageId).First();

            //Look up visibility
            Visibility visibility = visibilityRepository.FindBy(x => x.Id == vm.Visibility).First();

            //commit the potentially added entities, which will give us the Id's of the inserted items
            unitOfWork.Commit();

            //now enrol in new work cycle, where we can use the Id's of the entities we just added above
            codeSnippetRepository.EnrolInUnitOfWork(unitOfWork);

            Nullable<int> groupingForSnippet = null;
            if (grouping != null)
                groupingForSnippet = grouping.GroupId;

            #region Insert CodeSnippet

            CodeSnippet codeSnippet = null;
            if (!isEdit)
            {
                MembershipUser user = membershipService.GetUserByUserName(User.Identity.Name);
                Guid guid = Guid.Parse(user.ProviderUserKey.ToString());

                codeSnippet = new CodeSnippet(
                    vm.ActualCode,
                    category.CodeCategoryId,
                    vm.Description,
                    vm.Title,
                    groupingForSnippet,
                    language.LanguageId,
                    guid,
                    visibility, 0, 0, 0, DateTime.Now);

                codeSnippetRepository.Add(codeSnippet);
            }
            else
            {
                if (vm is EditSnippetViewModel)
                {
                    int snippetId = (vm as EditSnippetViewModel).CodeSnippetId;
                    codeSnippet = codeSnippetRepository.FindBy(x => x.CodeSnippetId == snippetId).FirstOrDefault();
                    if (codeSnippet != null)
                    {
                        codeSnippet.ActualCode = vm.ActualCode;
                        codeSnippet.CodeCategoryId = category.CodeCategoryId;
                        codeSnippet.Description = vm.Description;
                        codeSnippet.Title = vm.Title;
                        codeSnippet.GroupId = groupingForSnippet;
                        codeSnippet.LanguageId = language.LanguageId;
                        codeSnippet.Visibility = vm.Visibility;
                    }
                }
            }

            //commit the potentially added entities, which will give us the Id's of the inserted items
            unitOfWork.Commit();

            #endregion

            #region Add/edit tags is there are any

            if (codeSnippet != null)
            {

                //now enrol in new work cycle, where we can use the Id's of the entities we just added above
                codeTagRepository.EnrolInUnitOfWork(unitOfWork);

                //remove any existing tags
                if (isEdit)
                {
                    IQueryable<CodeTag> existingTags = codeTagRepository.FindBy(x => x.MatchingSnippetId == codeSnippet.CodeSnippetId);
                    foreach (CodeTag tag in existingTags)
                    {
                        codeTagRepository.Remove(tag);
                    }
                }

                //and add back in any current tags
                if (!String.IsNullOrEmpty(vm.Tags))
                {
                    //For each tag specified associate with the new CodeSnippet
                    string[] tags = vm.Tags.Split(new char[] { ';' });
                    foreach (string tag in tags)
                    {
                        codeTagRepository.Add(new CodeTag(tag, codeSnippet.CodeSnippetId));
                    }
                    unitOfWork.Commit();
                }
            }
            #endregion

            return codeSnippet;
        }




        private void RefreshAddModelStaticData(ISnippetViewModel vm)
        {
            languageRepository.EnrolInUnitOfWork(unitOfWork);
            visibilityRepository.EnrolInUnitOfWork(unitOfWork);
            vm.LanguageList = languageRepository.FindAll().ToList();
            vm.VisibilityList = visibilityRepository.FindAll().ToList();
        }


        private void AddLanguagesToOpenFromWebVm(OpenFromWebViewModel vm)
        {
            using (unitOfWork)
            {
                languageRepository.EnrolInUnitOfWork(unitOfWork);
                vm.LanguageList = languageRepository.FindAll().ToList();
            }
        }
        #endregion
    }
}
